Repository navigation
feat(local-model): a library of models on this Mac, with sizes, delete, add and switch - #804
Merged
Merged
Conversation
The local model window could hold one model. A second install was refused (409 already-installed), and a model a Remove kept could never be freed from the window again. The store can now hold several models, and exactly one of them is the model in use: the one the endpoint record lists, the runtime is started for and a session may be launched on. Server - LocalModelStateFile.library lists every model that passed the check (id, label, context, digest, size, coding agent). The service writes the entry when verify passes; the reducer carries it and remove drops it. An installed file of an earlier build counts its one model. - POST /api/local-model/install is allowed from installed: it adds a model, and the earlier one stays. A replaced job no longer deletes a model of the library, only an unchecked or partial download. - POST /api/local-model/use switches to a checked model: a job of the two steps verify and connect. The stored digest is compared before anything is loaded (model-changed otherwise). Nothing is downloaded. - DELETE /api/local-model/models deletes one model by its name, through the runtime's own delete, never by a path, never the model in use. - Adding and switching answer 409 sessions-open while a live session uses the endpoint. Until such a job ends installed, a session start is refused with the new code job-unfinished. - The view carries library (the state's entries joined with what the running runtime lists: ready, changed, missing, unverified) and store (the size of the blob folder). Both are cached and refreshed in the background, never read on the one-second poll. - With nothing installed, remove is offered while the store still holds models an earlier Remove kept. Window - The installed screen lists the models on this Mac with the context each was checked with, its state and its size on disk, with Use and Delete per row (Delete asks once, in the row) and Add a model. - In the chooser a model that is on this Mac already says so and is switched to without a download or a confirm screen. - A paused or failed job that has a checked model beside it shows the way back to that model. Every route is admin only in multi-user mode, like the existing ones. Not run on a Mac: the tests use fakes and the fake runtime, and the listing, store size and delete were checked against a real Ollama 0.13.0 on Linux, not the pinned 0.40.15 build for macOS. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The guide, the API reference, the invariants and the two CLAUDE files describe the library with one model in use: the state file's list, the three verbs and their refusals, the two cached listings of the view, and the window's list with its row actions. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
… the Stop A job started from a model the person had stopped runs the runtime, and the model it ends on is in use and running. The stored "stopped" flag stayed set, so that model would not have started with the server again. Both verbs now set it back once their job started, the way Start does. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This was referenced Nov 11, 2026
…his model" refusal
Seen on a MacBook (M5 Pro, 2026-11-12): with a worker of the codeman skill open on the
local model, Stop, Remove and a switch of models answered
Sessions are still using this model: . Close them first.
A session an agent starts has no name of its own (a quick-start without sessionName), and
the refusal listed session.name. The tab of such a session shows the last folder of its
working directory, else the first eight characters of its id, so the sentence now uses the
same label (sessionLabel() beside sessionNamesUsingEndpoint()).
The sentence has shipped since 1.50.0 for Stop and Remove. This branch adds it to Use and
to the install of another model, which is where a person with local workers open meets it.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…-button screen Seen on a MacBook (M5 Pro, 2026-11-12): pressing Use on a model in "Models on this Mac" ran the switch and then showed the screen that ends an install, "X is ready on this Mac" with Start a session alone. The list was gone, so deleting the model just switched away from meant closing the window and opening it again. That screen is right after a download: the person installed a model and wants a session. A switch is pressed IN the list by someone who is tidying it. A switch is a job without a `model` step, so the window now tells the two apart from the job it just watched and ends a switch on the whole installed screen: the list with "In use" on the other row, Add a model, Stop and Remove. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Ark0N
pushed a commit
that referenced
this pull request
Nov 12, 2026
…and switch Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Ark0N
pushed a commit
that referenced
this pull request
Nov 12, 2026
…f models, and a session without a name is named in the refusal Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Ark0N
pushed a commit
that referenced
this pull request
Nov 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
"Run a local model" is the window that installs a coding model on the Mac the Codeman server runs on, through a private Ollama runtime under the data directory. Until now it knew exactly one model: a second install was refused, and a model that Remove left on disk could never be freed from the window again.
This PR turns that into a library of models with one in use:
In the chooser opened from an installed state, a row that is already on this Mac says so and choosing it switches without a download. A failed add shows the way back to the previous model. On a phone each model row is two lines.
What stays exactly as it was
The feature's safety rules are unchanged, on purpose:
local-ollamastill lists exactly one model, the runtime still runs withOLLAMA_MAX_LOADED_MODELS=1, and the launch guard still starts a coding agent only on the model in use, after comparing its stored digest.sessions-openwith the session names), like Stop and Remove already were. Their coding agents were started on the current model, and two models would make the runtime swap tens of gigabytes per request.API (experimental routes, outside the stable
/api/v1contract)POST /api/local-model/install(now also frominstalled)sessions-open;already-installednow means this model at this context is the one in usePOST /api/local-model/use{ modelId }job-running,wrong-state,already-installed,sessions-openDELETE /api/local-model/models{ modelId }job-running,wrong-state,model-active,runtime-not-running; 500 the runtime did not delete itDELETE /api/local-modelidlewhile the store holds somethingGET /api/local-modelgains, insideinstall:library[]:{ id, label, context?, sizeBytes?, sizeGb?, active, verified, status, actions }.statusisready,changed,missingorunverified;actionsis a subset ofuse,delete.store:{ bytes, gb }, the real disk use of the model store.library: 'active' | 'ready'.How it is built
libraryholds every model that passed the check through Codeman, with its context, digest, size and coding agent. An installed file from an older version counts its one model.verifyandconnect, started by a new reducer eventuse. Every new state and event pair is a row in the transition table (45 pairs, 22 listed), and the "every state with every event" test covers all of them.name:tag, the name must be in the server's own listing, and only the runtime's own delete removes it.failedwith the new model. The view then offers "Use" on the other checked models so the person gets back to the previous one. Sessions stay refused until the state isinstalledagain, with a new sentence (job-unfinished) instead of the misleading "not running, start it first".Testing
npm test): see the CI run on this PR.test/local-model-*andtest/routes/local-model-*file, the i18n harvest, the custom model tests and both CLI id-branching guards. The end-to-end test drives the real modules over the fake runtime: install A, add B, switch back to A, delete B, the refusal for the model in use, thesessions-openrefusals.npm run test:browser -- test/local-model-ui.browser.test.ts(not part of the gate): 10 passed in Chromium, 4 of them new (sizes on one edge, no sideways jump when the Delete question opens, a long list scrolling from its top at 1440 and 390 px, two-line phone rows)./api/tagssizes summed to exactly the size of the blobs folder, and after a delete the listing dropped the model and the blobs were freed at once.Run on a real Mac (2026-11-12)
A MacBook Pro (M5 Pro, 48 GB, macOS 27.2) with the pinned runtime, Ollama 0.40.15. Its Codeman ran the tree of this stack (#803 to #808) merged onto 1.50.1. The window was driven by real clicks, from a Chromium on another machine through an SSH forward to the Mac's own server, so every state on screen was the Mac's.
sessions-open. The model in use could not be deleted, with or without sessions.Two bugs were found there and are fixed in this PR, each in its own commit, and each was seen fixed on the same Mac:
db81d55f: with a worker of the codeman skill open, the refusal read "Sessions are still using this model: . Close them first." A session an agent starts has no name of its own. The sentence now uses what the session's tab shows (its folder, else the short id). This sentence has shipped since 1.50.0 for Stop and Remove.9c2ba35f: after pressing Use, the window showed the one-button screen that ends an install, so the list was gone and deleting the other model meant closing the window and opening it again. A switch now ends on the list.Not proven, stated plainly
Known rough edges, left for a follow-up